﻿Option Strict Off

Imports System.Xml.Serialization
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Windows.Forms
Imports System.Reflection
Imports System.Linq


Public Class ArenalClient

#Region "Private members, constants, enums"

    'General constants
    Private Const DEFAULT_BASE As String = "http://localhost:8080/Arenal/api"
    Private Const TOKEN_HEADER_NAME As String = "arenal-token"
    Private Const DELETE_METHOD As String = "DELETE"

    'Publisher path
    Private Const PUBLISHERS_PATH As String = "/publishers"

    'Cards path
    Private Const CARDS_PATH As String = "/cards"
    Private Const CARDS_BY_PID_PATH As String = CARDS_PATH & "?pid="
    Private Const CARDS_VISIT_SUFFIX As String = "/visit"
    Private Const CARDS_PROVIDER_SUFFIX As String = "/provider"

    'Public data paths
    Private Const PUBLIC_BASE As String = "/public"
    Private Const REGIONS_PATH As String = PUBLIC_BASE & "/regions"
    Private Const PID_TYPES_PATH As String = PUBLIC_BASE & "/pidtypes"
    Private Const VISIT_TYPES_PATH As String = PUBLIC_BASE & "/visittypes"
    Private Const NHIF_EXAMS_PATH As String = PUBLIC_BASE & "/nhifpacks"
    Private Const SPEC_PATH As String = PUBLIC_BASE & "/specialities"
    Private Const COUNTRIES_PATH As String = PUBLIC_BASE & "/countries"
    Private Const PUBLISHER_ROLES_PATH As String = PUBLIC_BASE & "/publisherroles"
    Private Const PPROVIDERS_PATH As String = PUBLIC_BASE & "/providers"

    'Ares paths
    Private Const ARES_CARDS_PATH As String = "/cards"

    'Cached data
    Private mRegionsCache As Regions
    Private mPIDTypesCache As PIDTypes
    Private mVisitTypesCache As VisitTypes
    Private mNHIFPacksCache As NHIFPacks
    Private mSpecCache As Specialities
    Private mCountriesCache As Countries
    Private mPublisherRolesCache As PublisherRoles
    Private mProvidersCache As ServiceProviders

#End Region

#Region "Constructors and properties"

    Public Sub New(ServiceBaseURL As String, MyToken As String)
        Me.ServiceBase = ServiceBaseURL
        Me.Token = MyToken
    End Sub

    Public Property ServiceBase() As String = DEFAULT_BASE

    Public Property Token() As String = ""

#End Region

#Region "Helpers"

    Private Function GetXML(Obj As Object) As String
        If Obj Is Nothing Then Return Nothing
        Dim ser As XmlSerializer = New XmlSerializer(Obj.GetType)
        Using ms As New MemoryStream
            ser.Serialize(ms, Obj)
            Return Text.Encoding.UTF8.GetString(ms.ToArray)
        End Using
    End Function

    Private Shared Function GetObject(Of T)(Stream As Stream) As T
        If Stream Is Nothing Then Return Nothing
        Dim ser As New System.Xml.Serialization.XmlSerializer(GetType(T))
        Return DirectCast(ser.Deserialize(Stream), T)
    End Function

    Private Shared Function GetObject(Of T)(XML As String) As T
        If String.IsNullOrWhiteSpace(XML) Then Return Nothing
        Dim ser As New System.Xml.Serialization.XmlSerializer(GetType(T))
        Using sr As New IO.MemoryStream(Encoding.UTF8.GetBytes(XML))
            Return DirectCast(ser.Deserialize(sr), T)
        End Using
    End Function

    Private Shared Function GetArenalException(WebEx As WebException) As ArenalException
        Dim ae As New ArenalException("Unknwon error.", WebEx)
        If WebEx.Status = WebExceptionStatus.ProtocolError Then
            Try
                ae.ErrorInfo = GetObject(Of ArenalError)(WebEx.Response.GetResponseStream)
                ae.Message = ArenalException.ARENAL_ERROR
            Catch ex_int As Exception
                ae.Message = ArenalException.ERROR_RECEIVING_DATA
            End Try
        End If
        Return ae
    End Function

    Private Shared Sub CopyProperties(ByRef Source As Object, ByRef Destination As Object)
        If Source Is Nothing Then Throw New ArgumentNullException("Source is null (Nothing).")
        If Destination Is Nothing Then Throw New ArgumentNullException("Destination is null (Nothing).")
        If Source.GetType <> Destination.GetType Then Throw New ArgumentException("Objects are of different types.")
        For Each CurProp As PropertyInfo In Destination.GetType.GetProperties
            If CurProp.CanWrite Then
                CurProp.SetValue(Destination, CurProp.GetValue(Source, Nothing), Nothing)
            End If
        Next
    End Sub

    Private Function GetClient(Optional AdHocToken As String = Nothing) As WebClient
        Dim wc As New WebClient()
        If String.IsNullOrEmpty(AdHocToken) Then
            wc.Headers.Add(TOKEN_HEADER_NAME, Me.Token)
        Else
            wc.Headers.Add(TOKEN_HEADER_NAME, AdHocToken)
        End If
        wc.Headers.Add(HttpRequestHeader.Accept, "application/xml, text/xml")
        wc.Headers.Add(HttpRequestHeader.ContentType, "application/xml;charset=utf-8") 'When upload data
        wc.Headers.Add(HttpRequestHeader.UserAgent, "ArenalNetLibrary")
        wc.Encoding = System.Text.Encoding.UTF8
        Return wc
    End Function

#End Region

#Region "Publishers"

    Public Sub RegisterPublisher(ByRef Publ As Publisher, VendorCompositeToken As String)
        If Publ Is Nothing Then Throw New ArgumentNullException("Publ")
        'TODO: Validate VendorCompositeToken
        Dim raw As String = Nothing
        Using wc As WebClient = Me.GetClient(VendorCompositeToken)
            Try
                raw = wc.UploadString(
                    Me.ServiceBase & PUBLISHERS_PATH,
                    WebRequestMethods.Http.Post,
                    GetXML(Publ))
            Catch ex As WebException
                Throw GetArenalException(ex)
            End Try
        End Using
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            CopyProperties(GetObject(Of Publisher)(raw), Publ)
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try

    End Sub

    Public Function GetPublisher(PublisherID As Integer, PublisherTokenOrProviderComplex As String) As Publisher
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(PublisherTokenOrProviderComplex)
                wc.Headers.Remove(HttpRequestHeader.ContentType)
                raw = wc.DownloadString(Me.ServiceBase & PUBLISHERS_PATH & "/" & PublisherID.ToString)
            End Using
        Catch ex As WebException
            Throw GetArenalException(ex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            Return GetObject(Of Publisher)(raw)
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try
    End Function

    Public Sub UpdatePublisher(ByRef Publ As Publisher)
        If Publ Is Nothing Then Throw New ArgumentNullException("Publ")
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(Publ.Token)
                raw = wc.UploadString(
                    Me.ServiceBase & PUBLISHERS_PATH & "/" & Publ.ArenalID.ToString,
                    WebRequestMethods.Http.Put,
                    GetXML(Publ))
            End Using
        Catch ex As WebException
            Throw GetArenalException(ex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            CopyProperties(GetObject(Of Publisher)(raw), Publ)
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try

    End Sub

    Public Sub InvalidatePublisher(PublisherID As Integer, PublisherToken As String)
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(PublisherToken)
                raw = wc.UploadString(
                    Me.ServiceBase & PUBLISHERS_PATH & "/" & PublisherID.ToString,
                    DELETE_METHOD, String.Empty)
            End Using
        Catch ex As Exception
            Throw GetArenalException(ex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            Dim Res As Publisher = GetObject(Of Publisher)(raw)
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try
    End Sub

#End Region

#Region "Cards operations"

    Public Function SearchCards(PID As String, ConsumerCompositeToken As String) As Cards
        If String.IsNullOrWhiteSpace(PID) Then Throw New ArgumentNullException("PID")
        If String.IsNullOrWhiteSpace(ConsumerCompositeToken) Then Throw New ArgumentNullException("ConsumerComplexToken")
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(ConsumerCompositeToken)
                raw = wc.DownloadString(
                    Me.ServiceBase & CARDS_BY_PID_PATH & PID)
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            Dim rv As Cards = GetObject(Of Cards)(raw)
            rv.Items.Sort(Function(o1, o2)
                              Dim res As Integer = o1.PublisherID - o2.PublisherID
                              If res = 0 Then res = o1.CardNo - o2.CardNo
                              Return res
                          End Function)
            For Each c As Card In rv.Items
                c.Examinations.Sort(Function(o1, o2) String.Compare(o1.Code, o2.Code))
            Next
            Return rv
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try

    End Function

    Public Sub TakeCard(ByRef Card As Card, ConsumerCompositeToken As String)
        If Card Is Nothing Then Throw New ArgumentNullException("Card")
        If String.IsNullOrWhiteSpace(ConsumerCompositeToken) Then Throw New ArgumentNullException("ConsumerComplexToken")
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(ConsumerCompositeToken)
                raw = wc.UploadString(
                    Me.ServiceBase & CARDS_PATH & "/" & Card.ArenalID.ToString & CARDS_VISIT_SUFFIX,
                    WebRequestMethods.Http.Post,
                    "")
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            CopyProperties(GetObject(Of Card)(raw), Card)
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try
    End Sub

    Public Sub ReleaseCard(ByRef Card As Card)
        If Card Is Nothing Then Throw New ArgumentNullException("Card")
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(Card.ResultsToken)
                raw = wc.UploadString(
                    Me.ServiceBase & CARDS_PATH & "/" & Card.ArenalID.ToString & CARDS_VISIT_SUFFIX,
                    DELETE_METHOD, "")
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
    End Sub

    Public Sub RegisterCard(ByRef Card As Card)
        If Card Is Nothing Then Throw New ArgumentNullException("Card")
        'TODO: Check for publisher's token
        Dim raw As String = Nothing
        Try
            'Dim s As String = GetXML(Card)
            Using wc As WebClient = Me.GetClient()
                raw = wc.UploadString(
                    Me.ServiceBase & CARDS_PATH,
                    WebRequestMethods.Http.Post,
                    GetXML(Card))
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            CopyProperties(GetObject(Of Card)(raw), Card)
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try
    End Sub

    Public Function GetCard(CardID As Integer, CardTokenOrProviderComplex As String) As Card
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(CardTokenOrProviderComplex)
                wc.Headers.Remove(HttpRequestHeader.ContentType)
                raw = wc.DownloadString(
                    Me.ServiceBase & CARDS_PATH & "/" & CardID.ToString)
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            Dim rv As Card = GetObject(Of Card)(raw)
            rv.Examinations.Sort(Function(o1, o2) String.Compare(o1.Code, o2.Code))
            Return rv
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try

    End Function

    Public Sub UpdateCard(ByRef Card As Card)
        If Card Is Nothing Then Throw New ArgumentNullException("Card")
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(Card.Token)
                raw = wc.UploadString(
                    Me.ServiceBase & CARDS_PATH & "/" & Card.ArenalID.ToString,
                    WebRequestMethods.Http.Put,
                    GetXML(Card))
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            CopyProperties(GetObject(Of Card)(raw), Card)
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try
    End Sub

    Public Sub DeleteCard(CardID As Integer, CardToken As String)
        If String.IsNullOrWhiteSpace(CardToken) Then Throw New ArgumentNullException("CardToken")
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(CardToken)
                raw = wc.UploadString(
                    Me.ServiceBase & CARDS_PATH & "/" & CardID.ToString,
                    DELETE_METHOD, "")
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
    End Sub

    Public Function GetCardProvider(CardID As Integer, CardToken As String) As ServiceProvider
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(CardToken)
                wc.Headers.Remove(HttpRequestHeader.ContentType)
                raw = wc.DownloadString(
                    Me.ServiceBase & CARDS_PATH & "/" & CardID.ToString & CARDS_PROVIDER_SUFFIX)
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            Dim rv As ServiceProvider = GetObject(Of ServiceProvider)(raw)
            Return rv
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try

    End Function

    Public Function GetCardResultsDirect(ServerBase As String, CardID As Integer, CardResultToken As String) As Card
        Dim raw As String = Nothing
        Try
            Using wc As WebClient = Me.GetClient(CardResultToken)
                wc.Headers.Remove(HttpRequestHeader.ContentType)
                raw = wc.DownloadString(
                    ServerBase & ARES_CARDS_PATH & "/" & CardID.ToString)
            End Using
        Catch wex As WebException
            Throw GetArenalException(wex)
        End Try
        If String.IsNullOrWhiteSpace(raw) Then Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT)
        Try
            Dim rv As Card = GetObject(Of Card)(raw)
            rv.Examinations?.Sort(Function(o1, o2) String.Compare(o1.Code, o2.Code))
            rv.Examinations?.ForEach(
                Sub(o)
                    o.Results?.Sort(
                        Function(r1, r2) IIf(r1.Rank.HasValue, r2.Rank.Value, 0) - IIf(r2.Rank.HasValue, r2.Rank.Value, 0))
                End Sub)
            Return rv
        Catch ex As InvalidOperationException
            'Can't parse XML
            Throw New ArenalException(ArenalException.NO_DATA_OR_WRONG_FORMAT, ex)
        End Try

    End Function

    Public Function GetCardResults(CardID As Integer, CardToken As String) As Card
        Dim OrigCard As Card = Me.GetCard(CardID, CardToken)
        If OrigCard.ProviderID.HasValue Then
            Dim Prv As ServiceProvider = Me.GetCardProvider(CardID, CardToken)
            Dim ResCard = Me.GetCardResultsDirect(Prv.PrimaryAres, CardID, OrigCard.ResultsToken)
            'Checks if the card and patient is the same as original
            If OrigCard.CardNo = ResCard.CardNo AndAlso OrigCard.PIDType = ResCard.PIDType AndAlso OrigCard.PID = ResCard.PID Then
                Return ResCard
            Else
                'Patients doesn't match
                Throw New ArenalException(ArenalException.PAT_DOESNT_MATCH)
            End If
        Else
            'Card isn't taken
            Throw New ArenalException(ArenalException.CARD_ISNT_TAKEN)
        End If

    End Function


#End Region

#Region "Public lists"

    Public Function GetRegions() As Regions
        If Me.mRegionsCache IsNot Nothing Then
            Return Me.mRegionsCache
        End If
        Try
            Using wc As WebClient = Me.GetClient()
                Dim raw As String = wc.DownloadString(Me.ServiceBase & REGIONS_PATH)
                Me.mRegionsCache = GetObject(Of Regions)(raw)
                Me.mRegionsCache.Items.Sort(Function(o1, o2) String.Compare(o1.Code, o2.Code))
                For Each CurRegion As Region In Me.mRegionsCache.Items
                    raw = wc.DownloadString(Me.ServiceBase & REGIONS_PATH & "/" & CurRegion.Code)
                    Dim r As Region = GetObject(Of Region)(raw)
                    r.Municipalities.Sort(Function(o1, o2) String.Compare(o1.HRCode, o2.HRCode))
                    CurRegion.Municipalities = r.Municipalities
                Next
                Return Me.mRegionsCache
            End Using
        Catch wex As WebException
            Me.mRegionsCache = Nothing 'Prevent keeping partially loaded cache
            Throw GetArenalException(wex)
        End Try
    End Function

    Public Function GetPIDTypes() As PIDTypes
        If Me.mPIDTypesCache IsNot Nothing Then
            Return Me.mPIDTypesCache
        End If
        Try
            Using wc As WebClient = Me.GetClient()
                Dim raw As String = wc.DownloadString(Me.ServiceBase & PID_TYPES_PATH)
                Me.mPIDTypesCache = GetObject(Of PIDTypes)(raw)
                Me.mPIDTypesCache.Items.Sort(Function(o1, o2) o1.ID - o2.ID)
                Return Me.mPIDTypesCache
            End Using
        Catch wex As WebException
            Me.mPIDTypesCache = Nothing 'Prevent keeping partially loaded cache
            Throw GetArenalException(wex)
        End Try
    End Function

    Public Function GetVisitTypes() As VisitTypes
        If Me.mVisitTypesCache IsNot Nothing Then
            Return Me.mVisitTypesCache
        End If
        Try
            Using wc As WebClient = Me.GetClient()
                Dim raw As String = wc.DownloadString(Me.ServiceBase & VISIT_TYPES_PATH)
                Me.mVisitTypesCache = GetObject(Of VisitTypes)(raw)
                Me.mVisitTypesCache.Items.Sort(Function(o1, o2) o1.ID - o2.ID)
                Return Me.mVisitTypesCache
            End Using
        Catch wex As WebException
            Me.mVisitTypesCache = Nothing 'Prevent keeping partially loaded cache
            Throw GetArenalException(wex)
        End Try
    End Function

    Public Function GetPacks() As NHIFPacks
        If Me.mNHIFPacksCache IsNot Nothing Then
            Return Me.mNHIFPacksCache
        End If
        Dim ser As New XmlSerializer(GetType(NHIFPacks))
        Try
            Using wc As WebClient = Me.GetClient()
                Dim raw As String = wc.DownloadString(Me.ServiceBase & NHIF_EXAMS_PATH)
                Me.mNHIFPacksCache = GetObject(Of NHIFPacks)(raw)
                Me.mNHIFPacksCache.Items.Sort(Function(o1, o2) String.Compare(o1.Code, o2.Code))
                For Each CurPack As NHIFPack In Me.mNHIFPacksCache.Items
                    raw = wc.DownloadString(Me.ServiceBase & NHIF_EXAMS_PATH & "/" & CurPack.Code)
                    Dim r As NHIFPack = GetObject(Of NHIFPack)(raw)
                    r.NHIFExams.Sort(Function(o1, o2) String.Compare(o1.Code, o2.Code))
                    CurPack.NHIFExams = r.NHIFExams
                Next
                Return Me.mNHIFPacksCache
            End Using
        Catch wex As WebException
            Me.mNHIFPacksCache = Nothing 'Prevent keeping partially loaded cache
            Throw GetArenalException(wex)
        End Try
    End Function

    Public Function GetSpecialities() As Specialities
        If Me.mSpecCache IsNot Nothing Then
            Return Me.mSpecCache
        End If
        Try
            Using wc As WebClient = Me.GetClient()
                Dim raw As String = wc.DownloadString(Me.ServiceBase & SPEC_PATH)
                Me.mSpecCache = GetObject(Of Specialities)(raw)
                Me.mSpecCache.Items.Sort(Function(o1, o2) String.Compare(o1.Code, o2.Code))
                Return Me.mSpecCache
            End Using
        Catch wex As WebException
            Me.mSpecCache = Nothing 'Prevent keeping partially loaded cache
            Throw GetArenalException(wex)
        End Try
    End Function

    Public Function GetPublisherRoles() As PublisherRoles
        If Me.mPublisherRolesCache IsNot Nothing Then
            Return Me.mPublisherRolesCache
        End If
        Try
            Using wc As WebClient = Me.GetClient()
                Dim raw As String = wc.DownloadString(Me.ServiceBase & PUBLISHER_ROLES_PATH)
                Me.mPublisherRolesCache = GetObject(Of PublisherRoles)(raw)
                Me.mPublisherRolesCache.Items.Sort(Function(o1, o2) o1.ID - o2.ID)
                Return Me.mPublisherRolesCache
            End Using
        Catch wex As WebException
            Me.mPublisherRolesCache = Nothing 'Prevent keeping partially loaded cache
            Throw GetArenalException(wex)
        End Try
    End Function

    Public Function GetCountries() As Countries
        If Me.mCountriesCache IsNot Nothing Then
            Return Me.mCountriesCache
        End If
        Try
            Using wc As WebClient = Me.GetClient()
                Dim raw As String = wc.DownloadString(Me.ServiceBase & COUNTRIES_PATH)
                Me.mCountriesCache = GetObject(Of Countries)(raw)
                Me.mCountriesCache.Items.Sort(Function(o1, o2) String.Compare(o1.Code, o2.Code))
                Return Me.mCountriesCache
            End Using
        Catch wex As WebException
            Me.mCountriesCache = Nothing 'Prevent keeping partially loaded cache
            Throw GetArenalException(wex)
        End Try
    End Function

    Public Function GetProviders() As ServiceProviders
        If Me.mProvidersCache IsNot Nothing Then
            Return Me.mProvidersCache
        End If
        Try
            Using wc As WebClient = Me.GetClient()
                Dim raw As String = wc.DownloadString(Me.ServiceBase & PPROVIDERS_PATH)
                Me.mProvidersCache = GetObject(Of ServiceProviders)(raw)
                Me.mProvidersCache.Items.Sort(Function(o1, o2) String.Compare(o1.Name, o2.Name))
                Return Me.mProvidersCache
            End Using
        Catch wex As WebException
            Me.mProvidersCache = Nothing 'Prevent keeping partially loaded cache
            Throw GetArenalException(wex)
        End Try
    End Function

#End Region

End Class
